using Fiddler;
using System.Reflection;

namespace TangCaptureTraffic
{
    /// <summary>
    /// Fiddler应用程序核心类，负责管理HTTP/HTTPS代理服务和会话捕获
    /// </summary>
    internal class FiddlerApp
    {
        /// <summary>
        /// Fiddler核心代理服务监听端口号
        /// </summary>
        private const ushort fiddlerCoreListenPort = 8877;

        /// <summary>
        /// 存储所有捕获的网络会话的集合
        /// </summary>
        private static readonly ICollection<Session> sessions = new List<Session>();

        /// <summary>
        /// 最大会话数量限制，防止内存无限增长
        /// </summary>
        private static int MAX_SESSIONS => MemoryConfig.MaxSessions;

        /// <summary>
        /// 当会话数量达到最大值时，清理的会话数量
        /// </summary>
        private static int CLEANUP_COUNT => MemoryConfig.SessionCleanupCount;

        /// <summary>
        /// 用于保护sessions集合的读写锁，确保线程安全
        /// </summary>
        private static readonly ReaderWriterLockSlim sessionsLock = new ReaderWriterLockSlim();

        /// <summary>
        /// 当前程序集所在的目录路径
        /// </summary>
        private static readonly string assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

        /// <summary>
        /// 当新的网络会话被添加时触发的事件
        /// </summary>
        public static event Action<Session> SessionAdded;

        /// <summary>
        /// 当网络会话完成时触发的事件
        /// </summary>
        public static event Action<Session> SessionComplete;

        /// <summary>
        /// 附加Fiddler事件监听器，设置各种网络请求和响应的处理逻辑
        /// </summary>
        public static void AttachEventListeners()
        {
            // 重要提示：FiddlerCore在后台线程中调用事件处理程序
            // 如果需要与UI线程同步（例如向列表视图添加会话），必须在窗口句柄上调用委托的.Invoke方法
            // 如果要写入非线程安全的数据结构（如List<T>），必须使用Monitor或其他机制确保安全性

            // 订阅日志字符串事件，用于调试输出
            FiddlerApplication.Log.OnLogString += (o, lea) => Console.WriteLine($"** LogString: {lea.LogString}");

            // 订阅请求前事件，在发送请求之前进行处理
            FiddlerApplication.BeforeRequest += session =>
            {
                // 为了启用响应篡改功能，必须启用缓冲模式
                // 这允许FiddlerCore在BeforeResponse处理程序中修改响应，而不是将响应流式传输给客户端
                session.bBufferResponse = false;

                // 如果希望FiddlerCore通过自动回答Digest/Negotiate/NTLM/Kerberos挑战来自动认证，请设置此属性
                // session["X-AutoAuth"] = "(default)";

                // 会话标识
                session["flagname"] = Guid.NewGuid().ToString();

                // 请求时间
                session["requestTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

                try
                {
                    // 获取写锁以安全地添加会话到集合中
                    sessionsLock.EnterWriteLock();

                    // 检查会话数量是否超过限制
                    if (sessions.Count >= MAX_SESSIONS)
                    {
                        // 清理最旧的会话以释放内存
                        var sessionsToRemove = sessions.Take(CLEANUP_COUNT).ToList();
                        foreach (var oldSession in sessionsToRemove)
                        {
                            sessions.Remove(oldSession);
                        }

                        // 强制垃圾回收
                        GC.Collect();
                        GC.WaitForPendingFinalizers();

                        Console.WriteLine($"已清理 {CLEANUP_COUNT} 个旧会话，当前会话数: {sessions.Count}");
                    }

                    sessions.Add(session);
                    // 触发会话添加事件，通知订阅者有新会话
                    SessionAdded?.Invoke(session);
                }
                finally
                {
                    // 确保释放写锁
                    sessionsLock.ExitWriteLock();
                }
            };

            /*
            // 以下事件允许检查Fiddler读取的每个响应缓冲区。注意，这对绝大多数应用程序都没有用处，
            // 因为原始缓冲区几乎无用；它没有解压缩，包含头部和正文字节等。
            //
            // 此事件仅对少数需要访问原始、未处理字节流的应用程序有用
            Fiddler.FiddlerApplication.OnReadResponseBuffer += (o, rrea) =>
            {
                // 注意：arrDataBuffer是固定大小的数组。只应读取/操作字节0到iCountOfBytes。
                //
                // 仅为了演示，将每个字节转换为小写。注意这显然会破坏任何二进制内容。
                for (int i = 0; i < e.iCountOfBytes; i++)
                {
                    if ((e.arrDataBuffer[i] > 0x40) && (e.arrDataBuffer[i] < 0x5b))
                    {
                        e.arrDataBuffer[i] = (byte)(e.arrDataBuffer[i] + (byte)0x20);
                    }
                }
                Console.WriteLine(String.Format("为会话{1}读取了{0}个响应字节", e.iCountOfBytes, e.sessionOwner.id));
            }
            */

            // 订阅响应前事件，在响应返回给客户端之前进行处理
            Fiddler.FiddlerApplication.BeforeResponse += session =>
            {
                // 可以在此处记录响应信息
                // Console.WriteLine($"{session.id}:HTTP {session.responseCode} for {session.fullUrl}");

                // 取消注释以下两个语句可以解压缩/去分块HTTP响应，
                // 并随后修改任何HTTP响应以将"Telerik"替换为"Progress"。
                // 您还必须在上面的BeforeRequest事件处理程序中设置session.bBufferResponse = true。
                //
                //session.utilDecodeResponse(); session.utilReplaceInResponse("Telerik", "Progress");
            };

            // 订阅会话完成后事件
            FiddlerApplication.AfterSessionComplete += session =>
            {
                Console.WriteLine($"会话完成: {session.fullUrl}");

                // 响应时间
                session["responseTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

                int sessionsCount = 0;
                try
                {
                    // 获取读锁以安全地读取会话数量
                    sessionsLock.EnterReadLock();
                    sessionsCount = sessions.Count;

                    // 会话完成通知
                    SessionComplete?.Invoke(session);
                }
                finally
                {
                    // 确保释放读锁
                    sessionsLock.ExitReadLock();
                }

                // 如果没有会话则直接返回
                if (sessionsCount == 0)
                    return;
            };
        }

        /// <summary>
        /// 确保根证书已正确设置和信任，用于HTTPS流量的解密和捕获
        /// </summary>
        public static void EnsureRootCertificate()
        {
            // 创建BC证书制造器实例
            BCCertMaker.BCCertMaker certProvider = new BCCertMaker.BCCertMaker();
            CertMaker.oCertProvider = certProvider;

            // 首次运行时使用加载的提供程序生成根证书，然后在后续运行中重复使用它
            string rootCertificatePath = Path.Combine(assemblyDirectory, "..", "..", "RootCertificate.p12");
            string rootCertificatePassword = "S0m3T0pS3cr3tP4ssw0rd";

            // 检查根证书文件是否存在
            if (!File.Exists(rootCertificatePath))
            {
                // 如果不存在，创建新的根证书
                certProvider.CreateRootCertificate();
                // 将根证书和私钥写入PKCS12文件
                certProvider.WriteRootCertificateAndPrivateKeyToPkcs12File(rootCertificatePath, rootCertificatePassword);
            }
            else
            {
                // 如果存在，从PKCS12文件读取根证书和私钥
                certProvider.ReadRootCertificateAndPrivateKeyFromPkcs12File(rootCertificatePath, rootCertificatePassword);
            }

            // 一旦根证书设置完成，确保它被信任
            if (!CertMaker.IsRootCertificateTrusted())
            {
                // 将根证书添加到系统信任存储中
                CertMaker.TrustRootCertificate();
            }
        }

        /// <summary>
        /// 启动Fiddler核心代理服务，配置监听端口和各种代理选项
        /// </summary>
        public static void StartupFiddlerCore()
        {
            // 构建Fiddler核心启动设置
            FiddlerCoreStartupSettings startupSettings =
                new FiddlerCoreStartupSettingsBuilder()
                    .ListenOnPort(fiddlerCoreListenPort)  // 设置监听端口
                    .RegisterAsSystemProxy()              // 注册为系统代理
                    .ChainToUpstreamGateway()            // 链接到上游网关
                    .DecryptSSL()                        // 启用SSL解密
                    .EnableHTTP2()                       // 启用HTTP/2支持
                    .Build();

            // 使用配置的设置启动Fiddler应用程序
            FiddlerApplication.Startup(startupSettings);

            // 记录监听端点创建信息
            FiddlerApplication.Log.LogString($"在端口{CONFIG.ListenPort}上创建了监听端点");
        }

        /// <summary>
        /// 根据会话ID获取指定的网络会话对象
        /// </summary>
        /// <param name="Id">要查找的会话ID</param>
        /// <returns>匹配的会话对象，如果未找到则返回null</returns>
        public static Session GetSession(string flagname)
        {
            return sessions.FirstOrDefault(n => n["flagname"] == flagname);
        }

        public static void AddSession(Session session)
        {
            // 触发会话添加事件，通知订阅者有新会话
            sessions.Add(session);
        }


        /// <summary>
        /// 清空所有捕获的网络会话，用于重置会话列表
        /// </summary>
        public static void Clear()
        {
            try
            {
                sessionsLock.EnterWriteLock();

                sessions.Clear();

                // 强制垃圾回收
                GC.Collect();
                GC.WaitForPendingFinalizers();

                Console.WriteLine("已清空所有会话并释放内存");
            }
            finally
            {
                sessionsLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// 停止Fiddler代理服务并清理资源
        /// </summary>
        public static void Quit()
        {
            // 关闭Fiddler应用程序
            FiddlerApplication.Shutdown();
        }
    }
}